package com.core.family.mybatisplus;

/**
 * @program: YJ-Admin
 * @description:
 * @author: hl
 * @create: 2024-05-30 16:06
 **/

import cn.hutool.core.util.ClassUtil;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.base.MPJBaseService;
import com.github.yulichang.base.MPJBaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Configuration
@Slf4j
@AutoConfigureAfter(SpringUtil.class)
public class MapperAndServiceConfig {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    // 根据类名获取 bean name
    private String getBeanName(String className) {

        int index = className.lastIndexOf(".");
        String simpleClassName = index != -1 ? className.substring(index + 1) : className;

        char firstChar = simpleClassName.charAt(0);
        if (firstChar >= 'A' && firstChar <= 'Z') {
            firstChar -= 'A' - 'a';
        }
        return firstChar + simpleClassName.substring(1);
    }

    //  @PostConstruct
    public void setup() {
        //  String BASE_PATH = getClass().getPackage().getName().replace(".com.core.family.admin", "");
        String BASE_PATH = "com.core.family.admin";

        // 扫描数据库实体，根据实体创建对应 mapper、service
        Set<Class<?>> entityClassSet = ClassUtil.scanPackage(BASE_PATH + ".domain");
        if (entityClassSet.isEmpty()) {
            return;
        }
        for (Class<?> entityClass : entityClassSet) {
            // 只创建带有 MapAndService 注解的实体
            MapperAndService tableName = entityClass.getAnnotation(MapperAndService.class);
            if (tableName == null) {
                continue;
            }
            String mapperClassName = BASE_PATH + ".mapper" + "." + entityClass.getSimpleName() + "Mapper";
            String serviceImplClassName = BASE_PATH + ".service" + "." + entityClass.getSimpleName() + "ServiceImpl";
            String serviceClassName = BASE_PATH + ".service" + "." + entityClass.getSimpleName() + "Service";
            /* 创建 mapper */
            Class<?> mapperClass = new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(MPJBaseMapper.class, entityClass).build())
                    .name(mapperClassName)
                    .annotateType(AnnotationDescription.Builder.ofType(Mapper.class).build())
                    .make()
                    .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();
            MapperFactoryBean<?> factoryBean = new MapperFactoryBean<>(mapperClass);
            factoryBean.setSqlSessionFactory(sqlSessionFactory);
            sqlSessionFactory.getConfiguration().addMapper(mapperClass);
            try {
                SpringUtil.registerBean(getBeanName(mapperClassName), factoryBean.getObject());
                log.info("register mapper Bean -> name:{}", getBeanName(mapperClassName));
            } catch (Exception e) {
                e.printStackTrace();
            }

            /* 创建 service */
            Class<?> serviceClass = new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(MPJBaseService.class, entityClass).build())
                    .name(serviceClassName)
                    .make()
                    .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();

            List<AnnotationDescription> annotations = new ArrayList<>();
            annotations.add(AnnotationDescription.Builder.ofType(Service.class).build());
            annotations.add(AnnotationDescription.Builder.ofType(Transactional.class).build());
            Class<?> serviceImplClass = new ByteBuddy()
                    .subclass(TypeDescription.Generic.Builder.parameterizedType(MPJBaseServiceImpl.class, mapperClass, entityClass).build())
                    .implement(serviceClass)
                    .name(serviceImplClassName)
                    .annotateType(annotations)
                    .make()
                    .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();
            try {
                SpringUtil.registerBean(getBeanName(serviceImplClassName), serviceImplClass.newInstance());
                log.info("register Service Bean -> name:{}", getBeanName(serviceImplClassName));
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
